package ink.ykb.rpn;

import java.math.BigDecimal;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class InToPost {

  private String input;// 源字符串表达式

  private String output = "";// 后缀表达式

  private Stack<OptData> inStack;// 源表达式栈

  private Stack<OptData> outStackTemp;// 输出表达式栈中间临时数据区

  private Stack<OptData> outStack;// 输出表达式栈


  public InToPost(String in) {
    input = in;
    this.inStack = initInStack(in);
    this.outStackTemp = new Stack<>();
    this.outStack = new Stack<>();
  }

  public Stack<OptData> initInStack(String in) {
    Stack<OptData> stack = new Stack<>();

    // 去空格
    in = in.replaceAll("\\s+", "");

    Pattern p = Pattern.compile("\\+|-|\\*|/|\\(|\\)");
    Matcher m = p.matcher(in);
    
    int index = 0;
 
    while (m.find()) {
      
      int l = m.start();
      String opt = in.substring(index,l);
      if(opt != null && !opt.trim().equals("")) {
         OptData data = new OptData(opt, OptType.getOptType(opt));
         stack.push(data);
      }
      String opt2 = in.substring(l,l+1);
      if(opt2 != null && !opt2.trim().equals("")) {
        OptData data = new OptData(opt2, OptType.getOptType(opt2));
        stack.push(data);
      }
      
      index = l + 1;
    }
    
    if(index < in.length()) {
      String opt = in.substring(index,in.length());
      OptData data = new OptData(opt, OptType.getOptType(opt));
      stack.push(data);
    }
    
    return stack;
  }

  /**
   * 中缀表达式转成后缀表达式
   * 
   * @return
   */
  public void doTrans() {

    for (int j = inStack.size() - 1; j >= 0; j--) {
      OptData data = inStack.peekN(j);

      if (data.getType() == OptType.ADDITION || data.getType() == OptType.SUBTRACTION) {
        gotOper(data, 1);
      } else if (data.getType() == OptType.MULTIPLICATION || data.getType() == OptType.DIVISION) {
        gotOper(data, 2);
      } else if (data.getType() == OptType.OPENPAREN) {
        outStackTemp.push(data);
      } else if (data.getType() == OptType.CLOSEPAREN) {
        gotParen();
      } else {
        outStack.push(data);
      }
    }

    while (!outStackTemp.isEmpty()) {
      outStack.push(outStackTemp.pop());
    }

    // 转化输出
    for (int j = outStack.size() - 1; j >= 0; j--) {
      OptData data = outStack.peekN(j);
      if (data != null) {
        output = output + data.getOpt();
      }
    }

  }

  /**
   * 计算
   * 
   * @return
   */
  public BigDecimal gotParse() {
    Stack<OptData> stackTemp = new Stack<>();

    OptData data;
    BigDecimal interAns;
    OptData num1, num2;

    for (int j = outStack.size() - 1; j >= 0; j--) {
      data = outStack.peekN(j);
      if (data.getType() == OptType.NUMERAL) {
        stackTemp.push(data);
      } else {
        num2 = stackTemp.pop();
        num1 = stackTemp.pop();

        if (data.getType() == OptType.ADDITION) {
          interAns = new BigDecimal(num1.getOpt()).add(new BigDecimal(num2.getOpt()));
        } else if (data.getType() == OptType.SUBTRACTION) {
          interAns = new BigDecimal(num1.getOpt()).subtract(new BigDecimal(num2.getOpt()));
        } else if (data.getType() == OptType.MULTIPLICATION) {
          interAns = new BigDecimal(num1.getOpt()).multiply(new BigDecimal(num2.getOpt()));
        } else if (data.getType() == OptType.DIVISION) {
          interAns = new BigDecimal(num1.getOpt()).divide(new BigDecimal(num2.getOpt()), 15,
              BigDecimal.ROUND_HALF_UP);
        } else {
          interAns = new BigDecimal(0);
        }

        OptData optData = new OptData(interAns.toPlainString(), OptType.NUMERAL);
        stackTemp.push(optData);
      }
    }

    interAns = new BigDecimal(stackTemp.pop().getOpt());

    return interAns;
  }

  private void gotOper(OptData opThis, int prec1) {
    while (!outStackTemp.isEmpty()) {
      OptData data = outStackTemp.pop();
      if (data.getType() == OptType.OPENPAREN) {
        outStackTemp.push(data);
        break;
      } else {
        int prec2;
        if (data.getType() == OptType.ADDITION || data.getType() == OptType.SUBTRACTION) {
          prec2 = 1;
        } else {
          prec2 = 2;
        }
        if (prec2 < prec1) {
          outStackTemp.push(data);
          break;
        } else {
          outStack.push(data);
        }

      }
    }
    outStackTemp.push(opThis);
  }

  private void gotParen() {
    while (!outStackTemp.isEmpty()) {
      OptData data = outStackTemp.pop();
      if (data.getType() == OptType.OPENPAREN) {
        break;
      } else {
        outStack.push(data);
      }
    }
  }


  class OptData {
    private String opt;

    private OptType type;

    public String getOpt() {
      return opt;
    }

    public void setOpt(String opt) {
      this.opt = opt;
    }

    public OptType getType() {
      return type;
    }

    public void setType(OptType type) {
      this.type = type;
    }

    public OptData(String opt, OptType type) {
      super();
      this.opt = opt;
      this.type = type;
    }

    public OptData() {
      super();
    }

    @Override
    public String toString() {
      return "OptData [opt=" + opt + ", type=" + type + "]";
    }



  }

  enum OptType {

    NUMERAL("数字", "0"),

    ADDITION("加法", "+"),

    SUBTRACTION("减法", "-"),

    MULTIPLICATION("乘法", "*"),

    DIVISION("除法", "/"),

    OPENPAREN("左圆括号", "("),

    CLOSEPAREN("右圆括号", ")");

    private String name;

    private String value;


    private OptType(String name, String value) {
      this.name = name;
      this.value = value;
    }

    public static OptType getOptType(String value) {
      if (Pattern.matches("^[0-9]+.?[0-9]*$", value)) {
        return OptType.NUMERAL;
      }

      OptType[] values = OptType.values();

      for (OptType optType : values) {
        if (optType.value.equals(value)) {
          return optType;
        }
      }
      throw new RuntimeException("不支持此操作");
    }

    public String getName() {
      return name;
    }

    public void setName(String name) {
      this.name = name;
    }

  }

  public String getInput() {
    return input;
  }

  public Stack<OptData> getInStack() {
    return inStack;
  }

  public Stack<OptData> getOutStack() {
    return outStack;
  }

  public String getOutput() {
    return output;
  }



}
